/**
* \file DGVImageVTK.cpp
*
* \brief This file defines a class for displaying Images using VTK Image Viewer2.
* \author Shekhar S. Chandra, 2008
*
* This file is part of DGV Library.
*
* DGV is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* DGV is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with DGV.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "DGVImageVTK.h"

#include <QFileDialog>

#include "vtkPNGReader.h"
#include "vtkPNGWriter.h"
#include "vtkJPEGReader.h"
#include "vtkJPEGWriter.h"
#include "vtkTIFFReader.h"
#include "vtkTIFFWriter.h"
#include "vtkPNMReader.h"
#include "vtkPNMWriter.h"
#include "vtkBMPReader.h"
#include "vtkBMPWriter.h"
#include "vtkPostScriptWriter.h"
#include "vtkGDCMImageReader.h"
#include "vtkGDCMImageWriter.h"
#include "vtkXMLImageDataReader.h"
#include "vtkXMLImageDataWriter.h"
#include "vtkStructuredPoints.h"
#include "vtkStructuredPointsReader.h"
#include "vtkStructuredPointsWriter.h"
#include "vtkImageActor.h"
#include "vtkImageCast.h"
#include "vtkImageMapToWindowLevelColors.h"
#include "vtkImageClip.h"
#include "vtkImageAccumulate.h"
#include "vtkCamera.h"
#include "vtkCallbackCommand.h"
#include "vtkEventForwarderCommand.h"

#include "DGImage.hpp"

extern "C"
{
    #include "contrib/libtarga.h"
}

DGVImageVTK::DGVImageVTK(QWidget *parent) : QVTKWidget(parent)
{
    loaded = false;
    arrayLoaded = false;
    imageDataSource = false;
    imageDataLoaded = false;
    externalData = false;
    viewing = false;
    volume = false;
    complexData = false;
    consoleAssigned = false;
    verboseMode = true;
    enableVolumes = false;
    permuted = false;
    sliceInView = 0;
    outputPort = NULL;

    timerInterval = 0.0;
    timerId = -1;

    Blitz.alignmentForImages(true);

    ///Create Actions
    createActions();
    complexValuedMode(false);

	///Setup Connections
	createConnections();
}

DGVImageVTK::DGVImageVTK(QVector<QMenu*> &menus, QWidget *parent) : QVTKWidget(parent)
{
    loaded = false;
    arrayLoaded = false;
    imageDataSource = false;
    imageDataLoaded = false;
    externalData = false;
    viewing = false;
    volume = false;
    complexData = false;
    consoleAssigned = false;
    verboseMode = true;
    enableVolumes = true;
    sliceInView = 0;
    outputPort = NULL;

    timerInterval = 0.0;
    timerId = -1;

    Blitz.alignmentForImages(true);

    ///Create Actions
    createActions();
    complexValuedMode(false);

    ///Rmemeber to menus to add for later
    addToContextMenu(menus);

	///Setup Connections
	createConnections();
}

DGVImageVTK::~DGVImageVTK()
{
    ///Smart Deletion on
    ///permute, viewer and imageData
}

void DGVImageVTK::setName(QString filename)
{
    name = filename;
    setWindowTitle(strippedNamePrefix());

    emit nameChanged(strippedNamePrefix());
}

bool DGVImageVTK::openImage(QString filename)
{
	setName(filename);
    QWidget::setWindowTitle(strippedName());

    QFileInfo fi(name);
    QString extension = fi.suffix().toLower();

    printInfo("Extension of Image is " + extension);
	if(extension == "png")
		return openPNG();
	else if(extension == "jpg" || extension == "jpeg")
		return openJPEG();
	else if(extension == "tif" || extension == "tiff")
		return openTIFF();
	else if(extension == "pnm" || extension == "pgm" || extension == "pbm" || extension == "ppm")
		return openPNM();
	else if(extension == "bmp")
		return openBMP();
	else if(extension == "dcm" || extension == "dicom")
		return openDICOM();
    else if(extension == "vti")
        return openVTI();
    else if(extension == "vtk")
        return openVTK();
    else if(extension == "tga")
        return openTGA();
	else
	{
	    extension = fi.completeSuffix().toLower();
	    if( extension.contains("pnm.", Qt::CaseInsensitive) || extension.contains("ppm.", Qt::CaseInsensitive)
            || extension.contains("pgm.", Qt::CaseInsensitive) || extension.contains("pbm.", Qt::CaseInsensitive) )
            return openPNMVolume();
        else if( extension.contains("png.") )
            return openPNGVolume();
        else
            return false;
	}
}

bool DGVImageVTK::saveImage(QString filename)
{
    if(!filename.isEmpty())
    {
        QFileInfo fi(filename);
        QString extension = fi.suffix().toLower(), format;
        bool success = false;

        printInfo("Extension of Image will be " + extension);
        ///\todo Doesn't detect extension properly when using std::strings c_str() function.
        if(extension == "png")
        {
            format = "PNG format.";
            success = savePNG(filename);
        }
        else if(extension == "jpg" || extension == "jpeg")
        {
            format = "JPEG format.";
            success = saveJPEG(filename);
        }
        else if(extension == "tif" || extension == "tiff")
        {
            format = "TIFF format.";
            success = saveTIFF(filename);
        }
        else if(extension == "pnm" || extension == "pgm" || extension == "pbm" || extension == "ppm")
        {
            format = "PNM format.";
            success = savePNM(filename);
        }
        else if(extension == "bmp")
        {
            format = "BMP format.";
            success = saveBMP(filename);
        }
        else if(extension == "ps")
        {
            format = "PS format.";
            success = savePS(filename);
        }
        else if(extension == "vti")
        {
            format = "VTI format.";
            success = saveVTI(filename);
        }
        else if(extension == "vtk")
        {
            format = "VTK (legacy) format.";
            success = saveVTK(filename);
        }
        else if(extension == "dcm" || extension == "dicom")
        {
            format = "DICOM format.";
            success = saveDICOM(filename);
        }
        else if(extension == "tga")
        {
            format = "TGA format.";
            success = saveTGA(filename);
        }
        else
        {
            printError("Error: Unknown format. Not Saving.");
            success = false;
        }

        printInfo("Saving as file: " + filename + " having extension: " + extension + " as " + format);
        emit recentFileAvailable(filename);
        return success;
    }

    return false;
}

bool DGVImageVTK::saveImage()
{
    QFileDialog *fileSaver = new QFileDialog(this);
    QSettings settings("Shakes", "DGV");

    QString path = settings.value("recentPath").toString();
    QString filename = fileSaver->getSaveFileName(this,
                                   tr("Select File Name to Save"),
                                   path,
    tr("Images [8-Bit Colour] (*.png *.jpeg *.jpg *.bmp *.tiff *.tif *.tga *.dcm *.dicom);;Images [Arbitrary] (*.pgm *.vti *.vtk);;Vectorised (*.ps)"));

    return saveImage(filename);
}

bool DGVImageVTK::openStack(QString filename)
{
    setName(filename);
    QWidget::setWindowTitle(strippedName());

    QFileInfo fi(name);
    QString extension = fi.suffix().toLower();

    printInfo("Extension of Image is " + extension);
	if(extension == "png")
		return openPNGVolume();
	else if(extension == "jpg" || extension == "jpeg")
		return openJPEGVolume();
	else if(extension == "tif" || extension == "tiff")
		return openTIFFVolume();
	else if(extension == "pnm" || extension == "pgm" || extension == "pbm" || extension == "ppm")
		return openPNMVolume();
	else if(extension == "bmp")
		return openBMPVolume();
    else
        return false;
}

bool DGVImageVTK::saveStack()
{
    QSettings settings("Shakes", "DGV");
    QString path = settings.value("recentPath").toString(), filename;

    QFileDialog *fileSaver = new QFileDialog(this);

    filename = fileSaver->getSaveFileName(this,
                               tr("Enter First File Name (with extension)"),
                               path,
                               tr("Images (*.png *.pnm *.ppm *.pgm *.pbm *.jpeg *.jpg *.bmp *.tiff *.tif)"));

    if (!filename.isEmpty())
        return saveStack(filename);
    else
        return false;
}

bool DGVImageVTK::saveStack(QString filename)
{
    QFileInfo fi(filename);
    QString extension = fi.suffix().toLower();

    printInfo("Extension of Image is " + extension);
    if(extension == "png")
		return savePNGVolume(filename);
    else if(extension == "jpg" || extension == "jpeg")
		return saveJPEGVolume(filename);
	else if(extension == "tif" || extension == "tiff")
		return saveTIFFVolume(filename);
	else if(extension == "pnm" || extension == "pgm" || extension == "pbm" || extension == "ppm")
		return savePNMVolume(filename);
	else if(extension == "bmp")
		return saveBMPVolume(filename);
    else
        return false;
}

bool DGVImageVTK::openPNG()
{
	vtkPNGReader* reader = vtkPNGReader::New();

    if(reader->CanReadFile(name.toStdString().c_str()))
    {
        printInfo("Opening " + name + " as PNG File");
        reader->SetFileName(name.toStdString().c_str());
        reader->Update();
        imageData = reader->GetOutput();

        if(!viewing)
        {
            viewer = ImageViewer::New();
            viewing = true;
        }
        outputPort = reader->GetOutputPort();

        setupViewerFromReader(); //Loaded set in here
    }
    else
        loaded = false;

    reader->Delete();
	return loaded;
}

bool DGVImageVTK::savePNG(const QString filename)
{
	vtkPNGWriter* writer = vtkPNGWriter::New();

    if(!imageDataLoaded)
        arrayToData();

    ///Save Image Data with levels being viewed
    printInfo("Casting data to 8-bit Greyscale based on Window Level");
    writer->SetInputConnection(viewer->GetWindowLevel()->GetOutputPort());
    writer->SetFileName(filename.toStdString().c_str());
    writer->Write();
    writer->Delete();

	return true;
}

bool DGVImageVTK::openPNGVolume()
{
    vtkPNGReader* reader = vtkPNGReader::New();

	bool ok1, ok2, ok3;
	int start = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume Start:"), 0, -2147483647, 2147483647, 1, &ok1);
	int end = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume End (inclusive):"), 10, -2147483647, 2147483647, 1, &ok2);
    QString prefix = QInputDialog::getText(this, tr("Filename Pattern (in printf format)"),
                                          tr("Pattern:"), QLineEdit::Normal,
                                          "%s%i.png", &ok3);

    QString tmp, tmpName, tmp1, tmp2;
    QFileInfo stripper(name);
    tmp = stripper.suffix();
    tmpName = stripper.baseName();
    printInfo("Volume Stack Name to be used: " + tmpName);
    printInfo("Volume Stack to be read from: " + tmp1.setNum(start) + " - " + tmp2.setNum(end));

    if(reader->CanReadFile(name.toStdString().c_str()) && ok1 && ok2 && ok3)
    {
        int bounds[6] = {0};
        bounds[4] = start;
        bounds[5] = end;

        reader->SetDataExtent(bounds);
        tmpName.remove(QRegExp("\\d+$")); ///Remove Digits
        printInfo("Volume Stack Non-Digit Name to be used: " + tmpName);
        tmpName = stripper.path() + "/" + tmpName; ///Add path
        reader->SetFilePrefix(tmpName.toStdString().c_str());
        reader->SetFilePattern(prefix.toStdString().c_str());
        reader->Update();
        loaded = true;

        imageData = reader->GetOutput();
        imageData->Update();
        outputPort = reader->GetOutputPort();

        ///Setup volume plot
        volume = true;
        if(enableVolumes)
            generateVolume();

        if(!viewing)
        {
            viewer = ImageViewer::New();
            viewing = true;
        }

        setupViewerFromReader(); //Loaded set in here
    }
    else
        loaded = false;

    reader->Delete();
    return loaded;
}

bool DGVImageVTK::savePNGVolume(const QString filename)
{
    vtkPNGWriter* writer = vtkPNGWriter::New();

    if(!loaded)
        return false;

    if(!imageDataLoaded)
        arrayToData();

    int bounds[6] = {0};

    imageData->GetExtent(bounds);

	bool ok1, ok2, ok3;
	int start = QInputDialog::getInteger(this, tr("Write Volume stack to ranges."),
                                          tr("Enter Volume Start:"), 0, 0, bounds[4], 1, &ok1);
	int end = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume End (inclusive):"), bounds[5], 0, bounds[5], 1, &ok2);
    QString prefix = QInputDialog::getText(this, tr("Filename Pattern (in printf format)"),
                                          tr("Pattern:"), QLineEdit::Normal,
                                          "%s%i.png", &ok3);

    QString tmp, tmpName, tmp1, tmp2;
    QFileInfo stripper(filename);
    tmp = stripper.suffix();
    tmpName = stripper.baseName();
    printInfo("Volume Stack Name to be used: " + tmpName);
    printInfo("Volume Stack to be written from: " + tmp1.setNum(start) + " - " + tmp2.setNum(end));

    if(ok1 && ok2 && ok3)
    {
        tmpName.remove(QRegExp("\\d+$")); ///Remove Digits
        printInfo("Volume Stack Non-Digit Name to be used: " + tmpName);
        tmpName = stripper.path() + "/" + tmpName; ///Add path
        writer->SetInput(imageData);
        writer->SetFilePrefix(tmpName.toStdString().c_str());
        writer->SetFilePattern(prefix.toStdString().c_str());
        writer->Write();
        writer->Delete();

        return true;
    }
    else
        return false;
}

bool DGVImageVTK::openJPEG()
{
	vtkJPEGReader* reader = vtkJPEGReader::New();

    if(reader->CanReadFile(name.toStdString().c_str()))
    {
        reader->SetFileName(name.toStdString().c_str());
        reader->Update();
        imageData = reader->GetOutput();

        if(!viewing)
        {
            viewer = ImageViewer::New();
            viewing = true;
        }
        outputPort = reader->GetOutputPort();

        setupViewerFromReader(); //Loaded set in here
    }
    else
        loaded = false;

    reader->Delete();
	return loaded;
}

bool DGVImageVTK::saveJPEG(const QString filename)
{
	vtkJPEGWriter* writer = vtkJPEGWriter::New();

    if(!imageDataLoaded)
        arrayToData();

    ///Save Image Data with levels being viewed
    printInfo("Casting data to 8-bit Greyscale based on Window Level");
    writer->SetInputConnection(viewer->GetWindowLevel()->GetOutputPort());
    writer->SetFileName(filename.toStdString().c_str());
    writer->Write();
    writer->Delete();

	return true;
}

bool DGVImageVTK::openJPEGVolume()
{
    vtkJPEGReader* reader = vtkJPEGReader::New();

	bool ok1, ok2, ok3;
	int start = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume Start:"), 0, -2147483647, 2147483647, 1, &ok1);
	int end = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume End (inclusive):"), 10, -2147483647, 2147483647, 1, &ok2);
    QString prefix = QInputDialog::getText(this, tr("Filename Pattern (in printf format)"),
                                          tr("Pattern:"), QLineEdit::Normal,
                                          "%s%i.jpg", &ok3);

    QString tmp, tmpName, tmp1, tmp2;
    QFileInfo stripper(name);
    tmp = stripper.suffix();
    tmpName = stripper.baseName();
    printInfo("Volume Stack Name to be used: " + tmpName);
    printInfo("Volume Stack to be read from: " + tmp1.setNum(start) + " - " + tmp2.setNum(end));

    if(reader->CanReadFile(name.toStdString().c_str()) && ok1 && ok2 && ok3)
    {
        int bounds[6] = {0};
        bounds[4] = start;
        bounds[5] = end;

        reader->SetDataExtent(bounds);
        tmpName.remove(QRegExp("\\d+$")); ///Remove Digits
        printInfo("Volume Stack Non-Digit Name to be used: " + tmpName);
        tmpName = stripper.path() + "/" + tmpName; ///Add path
        reader->SetFilePrefix(tmpName.toStdString().c_str());
        reader->SetFilePattern(prefix.toStdString().c_str());
        reader->Update();
        loaded = true;

        imageData = reader->GetOutput();
        imageData->Update();
        outputPort = reader->GetOutputPort();

        ///Setup volume plot
        volume = true;
        if(enableVolumes)
            generateVolume();

        if(!viewing)
        {
            viewer = ImageViewer::New();
            viewing = true;
        }

        setupViewerFromReader(); //Loaded set in here
    }
    else
        loaded = false;

    reader->Delete();
    return loaded;
}

bool DGVImageVTK::saveJPEGVolume(const QString filename)
{
    vtkJPEGWriter* writer = vtkJPEGWriter::New();

    if(!loaded)
        return false;

    if(!imageDataLoaded)
        arrayToData();

    int bounds[6] = {0};

    imageData->GetExtent(bounds);

	bool ok1, ok2, ok3;
	int start = QInputDialog::getInteger(this, tr("Write Volume stack to ranges."),
                                          tr("Enter Volume Start:"), 0, 0, bounds[4], 1, &ok1);
	int end = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume End (inclusive):"), bounds[5], 0, bounds[5], 1, &ok2);
    QString prefix = QInputDialog::getText(this, tr("Filename Pattern (in printf format)"),
                                          tr("Pattern:"), QLineEdit::Normal,
                                          "%s%i.jpg", &ok3);

    QString tmp, tmpName, tmp1, tmp2;
    QFileInfo stripper(filename);
    tmp = stripper.suffix();
    tmpName = stripper.baseName();
    printInfo("Volume Stack Name to be used: " + tmpName);
    printInfo("Volume Stack to be written from: " + tmp1.setNum(start) + " - " + tmp2.setNum(end));

    if(ok1 && ok2 && ok3)
    {
        tmpName.remove(QRegExp("\\d+$")); ///Remove Digits
        printInfo("Volume Stack Non-Digit Name to be used: " + tmpName);
        tmpName = stripper.path() + "/" + tmpName; ///Add path
        writer->SetInput(imageData);
        writer->SetFilePrefix(tmpName.toStdString().c_str());
        writer->SetFilePattern(prefix.toStdString().c_str());
        writer->Write();
        writer->Delete();

        return true;
    }
    else
        return false;
}

bool DGVImageVTK::openTIFF()
{
	vtkTIFFReader* reader = vtkTIFFReader::New();

    if(reader->CanReadFile(name.toStdString().c_str()))
    {
        reader->SetFileName(name.toStdString().c_str());
        reader->Update();
        imageData = reader->GetOutput();

        if(!viewing)
        {
            viewer = ImageViewer::New();
            viewing = true;
        }
        outputPort = reader->GetOutputPort();

        setupViewerFromReader(); //Loaded set in here
    }
    else
        loaded = false;

    reader->Delete();
	return loaded;
}

bool DGVImageVTK::saveTIFF(const QString filename)
{
	vtkTIFFWriter* writer = vtkTIFFWriter::New();

    if(!imageDataLoaded)
        arrayToData();

    ///Save Image Data with levels being viewed
    printInfo("Casting data to 8-bit Greyscale based on Window Level");
    writer->SetInputConnection(viewer->GetWindowLevel()->GetOutputPort());
    writer->SetFileName(filename.toStdString().c_str());
    writer->Write();
    writer->Delete();

	return true;
}

bool DGVImageVTK::openTIFFVolume()
{
    vtkTIFFReader* reader = vtkTIFFReader::New();

	bool ok1, ok2, ok3;
	int start = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume Start:"), 0, -2147483647, 2147483647, 1, &ok1);
	int end = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume End (inclusive):"), 10, -2147483647, 2147483647, 1, &ok2);
    QString prefix = QInputDialog::getText(this, tr("Filename Pattern (in printf format)"),
                                          tr("Pattern:"), QLineEdit::Normal,
                                          "%s%i.tif", &ok3);

    QString tmp, tmpName, tmp1, tmp2;
    QFileInfo stripper(name);
    tmp = stripper.suffix();
    tmpName = stripper.baseName();
    printInfo("Volume Stack Name to be used: " + tmpName);
    printInfo("Volume Stack to be written from: " + tmp1.setNum(start) + " - " + tmp2.setNum(end));

    if(reader->CanReadFile(name.toStdString().c_str()) && ok1 && ok2 && ok3)
    {
        int bounds[6] = {0};
        bounds[4] = start;
        bounds[5] = end;

        reader->SetDataExtent(bounds);
        tmpName.remove(QRegExp("\\d+$")); ///Remove Digits
        printInfo("Volume Stack Non-Digit Name to be used: " + tmpName);
        tmpName = stripper.path() + "/" + tmpName; ///Add path
        reader->SetFilePrefix(tmpName.toStdString().c_str());
        reader->SetFilePattern(prefix.toStdString().c_str());
        reader->Update();
        loaded = true;

        imageData = reader->GetOutput();
        imageData->Update();
        outputPort = reader->GetOutputPort();

        ///Setup volume plot
        volume = true;
        if(enableVolumes)
            generateVolume();

        if(!viewing)
        {
            viewer = ImageViewer::New();
            viewing = true;
        }

        setupViewerFromReader(); //Loaded set in here
    }
    else
        loaded = false;

    reader->Delete();
    return loaded;
}

bool DGVImageVTK::saveTIFFVolume(const QString filename)
{
    vtkTIFFWriter* writer = vtkTIFFWriter::New();

    if(!loaded)
        return false;

    if(!imageDataLoaded)
        arrayToData();

    int bounds[6] = {0};

    imageData->GetExtent(bounds);

	bool ok1, ok2, ok3;
	int start = QInputDialog::getInteger(this, tr("Write Volume stack to ranges."),
                                          tr("Enter Volume Start:"), 0, 0, bounds[4], 1, &ok1);
	int end = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume End (inclusive):"), bounds[5], 0, bounds[5], 1, &ok2);
    QString prefix = QInputDialog::getText(this, tr("Filename Pattern (in printf format)"),
                                          tr("Pattern:"), QLineEdit::Normal,
                                          "%s%i.tif", &ok3);

    QString tmp, tmpName, tmp1, tmp2;
    QFileInfo stripper(filename);
    tmp = stripper.suffix();
    tmpName = stripper.baseName();
    printInfo("Volume Stack Name to be used: " + tmpName);
    printInfo("Volume Stack to be read from: " + tmp1.setNum(start) + " - " + tmp2.setNum(end));

    if(ok1 && ok2 && ok3)
    {
        tmpName.remove(QRegExp("\\d+$")); ///Remove Digits
        printInfo("Volume Stack Non-Digit Name to be used: " + tmpName);
        tmpName = stripper.path() + "/" + tmpName; ///Add path
        writer->SetInput(imageData);
        writer->SetFilePrefix(tmpName.toStdString().c_str());
        writer->SetFilePattern(prefix.toStdString().c_str());
        writer->Write();
        writer->Delete();

        return true;
    }
    else
        return false;
}

bool DGVImageVTK::openPNM()
{
    DGImage<imageType> imageReader;
	vtkPNMReader* reader = vtkPNMReader::New();

	if(imageReader.isPNMBinaryFormat(name.toStdString()))
	{
        printInfo("Binary Format: Using VTK Reader");

        if(reader->CanReadFile(name.toStdString().c_str()))
        {
            reader->SetFileName(name.toStdString().c_str());
            reader->Update();
            imageData = reader->GetOutput();

            if(!viewing)
            {
                viewer = ImageViewer::New();
                viewing = true;
            }
            outputPort = reader->GetOutputPort();

            setupViewerFromReader(); //Loaded set in here
        }
        else
        {
            printError("Error loading Binary PNM File");
            loaded = false;
        }

        reader->Delete();
	}
	else
	{
	    int grey = 0;
        printInfo("ASCII Format: Using Custom Reader");
        imageArray = 0;
        if(imageReader.readPNM(name.toStdString(),imageArray,grey))
        {
            arrayLoaded = true;
            loaded = true;
            DGVImageVTK::arrayToData();
            imageDataSource = false;
            generateImage();
            return true;
        }
        else
        {
            printError("Error loading ASCII PNM File");
            return false;
        }
	}

	return loaded;
}

bool DGVImageVTK::savePNM(const QString filename)
{
    DGImage<imageType> imageWriter;

    if(!arrayLoaded)
        dataToArray();

    imageWriter.writePNM(imageArray,Blitz.getMax(),filename.toStdString());

	return true;
}

bool DGVImageVTK::openPNMVolume()
{
    DGImage<imageType> imageReader;
	vtkPNMReader* reader = vtkPNMReader::New();

	bool ok1, ok2, ok3;
	int start = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume Start:"), 0, -2147483647, 2147483647, 1, &ok1);
	int end = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume End (inclusive):"), 10, -2147483647, 2147483647, 1, &ok2);
    QString prefix = QInputDialog::getText(this, tr("Filename Pattern (in printf format)"),
                                          tr("Pattern:"), QLineEdit::Normal,
                                          "%s%i.pgm", &ok3);

	if(imageReader.isPNMBinaryFormat(name.toStdString()) && ok1 && ok2 && ok3)
	{
        printInfo("Binary Format: Using VTK Reader");

        QString tmp, tmpName, tmp1, tmp2;
        QFileInfo stripper(name);

        ///Strip name and number
        tmp = stripper.suffix();
        tmpName = stripper.baseName();
        printInfo("Volume Stack Name to be used: " + tmpName);
        printInfo("Volume Stack to be read from: " + tmp1.setNum(start) + " - " + tmp2.setNum(end));

        if(reader->CanReadFile(name.toStdString().c_str()))
        {
            int bounds[6] = {0};
            bounds[4] = start;
            bounds[5] = end;

            reader->SetDataExtent(bounds);
            tmpName.remove(QRegExp("\\d+$")); ///Remove Digits
            printInfo("Volume Stack Non-Digit Name to be used: " + tmpName);
            tmpName = stripper.path() + "/" + tmpName; ///Add path
            reader->SetFilePrefix(tmpName.toStdString().c_str());
            reader->SetFilePattern(prefix.toStdString().c_str());
            reader->Update();
            loaded = true;

            imageData = reader->GetOutput();
            imageData->Update();
            outputPort = reader->GetOutputPort();

            ///Setup volume plot
            volume = true;
            if(enableVolumes)
                generateVolume();

            if(!viewing)
            {
                viewer = ImageViewer::New();
                viewing = true;
            }

            setupViewerFromReader(); //Loaded set in here
        }
        else
            loaded = false;

        reader->Delete();
	}
	else
	{
	    int grey = 0;
	    Array<imageType,3> tmpArray;
	    QFileInfo fi(name);
	    QString ext = fi.suffix(), tmpName;
	    bool success = true;

        printInfo("ASCII Format: Using Custom Reader");
        imageArray = 0;

        tmpName = name;
        tmpName.truncate(tmpName.size()-ext.size()-1); ///Remove '.<number>'
        ext = ".";
        ext += numToString(start).c_str();
        tmpName += ext;
        if(!imageReader.readPNM(tmpName.toStdString(),imageArray,grey))
            return false;
        else
        {
            tmpArray.resizeAndPreserve(imageArray.rows(),imageArray.cols(),end-start+1);
            tmpArray(Range::all(),Range::all(),0) = imageArray;
            for(int j = start+1; j <= end; j ++)
            {
                tmpName.truncate(tmpName.size()-ext.size()); ///Remove '.<number>'
                ext = ".";
                ext += numToString(j).c_str();
                tmpName += ext;
                success = imageReader.readPNM(tmpName.toStdString(),imageArray,grey);
                if(!success)
                    break;
                tmpArray(Range::all(),Range::all(),j-start) = imageArray;
            }
            loaded = true;
            imageData = Blitz.arrayToVTKImageData(tmpArray);
            imageDataLoaded = true;
            imageDataSource = true;
            arrayLoaded = false;
            refresh();

            return true;
        }
	}

	return loaded;
}

bool DGVImageVTK::savePNMVolume(const QString filename)
{
    vtkPNMWriter* writer = vtkPNMWriter::New();

    if(!loaded)
        return false;

    if(!imageDataLoaded)
        arrayToData();

    int bounds[6] = {0};

    imageData->GetExtent(bounds);

	bool ok1, ok2, ok3;
	int start = QInputDialog::getInteger(this, tr("Write Volume stack to ranges."),
                                          tr("Enter Volume Start:"), 0, 0, bounds[4], 1, &ok1);
	int end = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume End (inclusive):"), bounds[5], 0, bounds[5], 1, &ok2);
    QString prefix = QInputDialog::getText(this, tr("Filename Pattern (in printf format)"),
                                          tr("Pattern:"), QLineEdit::Normal,
                                          "%s%i.pgm", &ok3);

    QString tmp, tmpName, tmp1, tmp2;
    QFileInfo stripper(filename);
    tmp = stripper.suffix();
    tmpName = stripper.baseName();
    printInfo("Volume Stack Name to be used: " + tmpName);
    printInfo("Volume Stack to be written from: " + tmp1.setNum(start) + " - " + tmp2.setNum(end));

    if(ok1 && ok2 && ok3)
    {
        tmpName.remove(QRegExp("\\d+$")); ///Remove Digits
        printInfo("Volume Stack Non-Digit Name to be used: " + tmpName);
        tmpName = stripper.path() + "/" + tmpName; ///Add path
        writer->SetInput(imageData);
        writer->SetFilePrefix(tmpName.toStdString().c_str());
        writer->SetFilePattern(prefix.toStdString().c_str());
        writer->Write();
        writer->Delete();

        return true;
    }
    else
        return false;
}

bool DGVImageVTK::openBMP()
{
	vtkBMPReader* reader = vtkBMPReader::New();

    if(reader->CanReadFile(name.toStdString().c_str()))
    {
        reader->SetFileName(name.toStdString().c_str());
        reader->Update();
        imageData = reader->GetOutput();

        if(!viewing)
        {
            viewer = ImageViewer::New();
            viewing = true;
        }
        outputPort = reader->GetOutputPort();

        setupViewerFromReader(); //Loaded set in here
    }
    else
        loaded = false;

    reader->Delete();
	return loaded;
}

bool DGVImageVTK::saveBMP(const QString filename)
{
	vtkBMPWriter* writer = vtkBMPWriter::New();

    if(!imageDataLoaded)
        arrayToData();

    ///Save Image Data with levels being viewed
    printInfo("Casting data to 8-bit Greyscale based on Window Level");
    writer->SetInputConnection(viewer->GetWindowLevel()->GetOutputPort());
    writer->SetFileName(filename.toStdString().c_str());
    writer->Write();
    writer->Delete();

	return true;
}

bool DGVImageVTK::openBMPVolume()
{
    vtkBMPReader* reader = vtkBMPReader::New();

	bool ok1, ok2, ok3;
	int start = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume Start:"), 0, -2147483647, 2147483647, 1, &ok1);
	int end = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume End (inclusive):"), 10, -2147483647, 2147483647, 1, &ok2);
    QString prefix = QInputDialog::getText(this, tr("Filename Pattern (in printf format)"),
                                          tr("Pattern:"), QLineEdit::Normal,
                                          "%s%i.bmp", &ok3);

    QString tmp, tmpName, tmp1, tmp2;
    QFileInfo stripper(name);
    tmp = stripper.suffix();
    tmpName = stripper.baseName();
    printInfo("Volume Stack Name to be used: " + tmpName);
    printInfo("Volume Stack to be read from: " + tmp1.setNum(start) + " - " + tmp2.setNum(end));

    if(reader->CanReadFile(name.toStdString().c_str()) && ok1 && ok2 && ok3)
    {
        int bounds[6] = {0};
        bounds[4] = start;
        bounds[5] = end;

        reader->SetDataExtent(bounds);
        tmpName.remove(QRegExp("\\d+$")); ///Remove Digits
        printInfo("Volume Stack Non-Digit Name to be used: " + tmpName);
        tmpName = stripper.path() + "/" + tmpName; ///Add path
        reader->SetFilePrefix(tmpName.toStdString().c_str());
        reader->SetFilePattern(prefix.toStdString().c_str());
        reader->Update();
        loaded = true;

        imageData = reader->GetOutput();
        imageData->Update();
        outputPort = reader->GetOutputPort();

        ///Setup volume plot
        volume = true;
        if(enableVolumes)
            generateVolume();

        if(!viewing)
        {
            viewer = ImageViewer::New();
            viewing = true;
        }

        setupViewerFromReader(); //Loaded set in here
    }
    else
        loaded = false;

    reader->Delete();
    return loaded;
}

bool DGVImageVTK::saveBMPVolume(const QString filename)
{
    vtkBMPWriter* writer = vtkBMPWriter::New();

    if(!loaded)
        return false;

    if(!imageDataLoaded)
        arrayToData();

    int bounds[6] = {0};

    imageData->GetExtent(bounds);

	bool ok1, ok2, ok3;
	int start = QInputDialog::getInteger(this, tr("Write Volume stack to ranges."),
                                          tr("Enter Volume Start:"), 0, 0, bounds[4], 1, &ok1);
	int end = QInputDialog::getInteger(this, tr("Read Volume stack from ranges."),
                                          tr("Enter Volume End (inclusive):"), bounds[5], 0, bounds[5], 1, &ok2);
    QString prefix = QInputDialog::getText(this, tr("Filename Pattern (in printf format)"),
                                          tr("Pattern:"), QLineEdit::Normal,
                                          "%s%i.bmp", &ok3);

    QString tmp, tmpName, tmp1, tmp2;
    QFileInfo stripper(filename);
    tmp = stripper.suffix();
    tmpName = stripper.baseName();
    printInfo("Volume Stack Name to be used: " + tmpName);
    printInfo("Volume Stack to be written from: " + tmp1.setNum(start) + " - " + tmp2.setNum(end));

    if(ok1 && ok2 && ok3)
    {
        tmpName.remove(QRegExp("\\d+$")); ///Remove Digits
        printInfo("Volume Stack Non-Digit Name to be used: " + tmpName);
        tmpName = stripper.path() + "/" + tmpName; ///Add path
        writer->SetInput(imageData);
        writer->SetFilePrefix(tmpName.toStdString().c_str());
        writer->SetFilePattern(prefix.toStdString().c_str());
        writer->Write();
        writer->Delete();

        return true;
    }
    else
        return false;
}

bool DGVImageVTK::savePS(const QString filename)
{
	vtkPostScriptWriter* writer = vtkPostScriptWriter::New();

    if(!imageDataLoaded)
        arrayToData();

    ///Save Image Data with levels being viewed
    printInfo("Casting data to 8-bit Greyscale based on Window Level");
    writer->SetInputConnection(viewer->GetWindowLevel()->GetOutputPort());
    writer->SetFileName(filename.toStdString().c_str());
    writer->Write();
    writer->Delete();

	return true;
}

bool DGVImageVTK::openDICOM()
{
	vtkGDCMImageReader* reader = vtkGDCMImageReader::New();

    if(reader->CanReadFile(name.toStdString().c_str()))
    {
        reader->SetFileName(name.toStdString().c_str());
        reader->Update();
        imageData = reader->GetOutput();

        if(!viewing)
        {
            viewer = ImageViewer::New();
            viewing = true;
        }
        outputPort = reader->GetOutputPort();

        setupViewerFromReader(); //Loaded set in here

        reader->Delete();
    }
    else
    {
        reader->Delete();
        loaded = false;
    }

	return loaded;
}

bool DGVImageVTK::saveDICOM(const QString filename)
{
	vtkGDCMImageWriter* writer = vtkGDCMImageWriter::New();

    if(!imageDataLoaded)
        arrayToData();

    ///Save Image Data with levels being viewed
    printInfo("Casting data to 8-bit Greyscale based on Window Level");
    writer->SetInputConnection(viewer->GetWindowLevel()->GetOutputPort());
    writer->SetFileName(filename.toStdString().c_str());
    writer->Write();
    writer->Delete();

	return true;
}

bool DGVImageVTK::openVTI()
{
    vtkXMLImageDataReader *reader = vtkXMLImageDataReader::New();

    if(reader->CanReadFile(name.toStdString().c_str()))
    {
        int bounds[6];
        QString tmp1, tmp2;

        reader->SetFileName(name.toStdString().c_str());
        reader->Update();
        imageData = reader->GetOutput();
        outputPort = reader->GetOutputPort();
        imageData->GetExtent(bounds);
        printInfo("Image Size: " + tmp1.setNum(bounds[1]) + "x" + tmp2.setNum(bounds[3]));

        if(bounds[5] > 1) ///If 3rd image then display volume plot too
        {
            volume = true;
            if(enableVolumes)
                generateVolume();
        }
        else
            printInfo("Didn't find 3D Image Data. Not showing volume plot.");

        if(!viewing)
        {
            viewer = ImageViewer::New();
            viewing = true;
        }

        setupViewerFromReader(); //Loaded set in here
    }
    else
        loaded = false;

    reader->Delete();
	return loaded;
}

bool DGVImageVTK::saveVTI(const QString filename)
{
	vtkXMLImageDataWriter* writer = vtkXMLImageDataWriter::New();

    if(!imageDataLoaded)
        arrayToData();

    writer->SetInput(imageData);
    writer->SetFileName(filename.toStdString().c_str());
    writer->SetDataModeToBinary();
    writer->Write();
    writer->Delete();

	return true;
}

bool DGVImageVTK::openVTK()
{
    vtkStructuredPointsReader *reader = vtkStructuredPointsReader::New();

    ///\todo Check if file provided is readable. CanReadFile is not member of vtkStructuredPointsReader.
    //if(reader->CanReadFile(name.toStdString().c_str()))
    //{
        int bounds[6];
        QString tmp1, tmp2;

        reader->SetFileName(name.toStdString().c_str());
        reader->Update();
        imageData = reader->GetOutput();
        outputPort = reader->GetOutputPort();
        imageData->GetExtent(bounds);
        printInfo("Image Size: " + tmp1.setNum(bounds[1]) + "x" + tmp2.setNum(bounds[3]));

        if(bounds[5] > 1) ///If 3rd image then display volume plot too
        {
            volume = true;
            if(enableVolumes)
                generateVolume();
        }
        else
            printInfo("Didn't find 3D Image Data. Not showing volume plot.");

        if(!viewing)
        {
            viewer = ImageViewer::New();
            viewing = true;
        }

        setupViewerFromReader(); //Loaded set in here
    //}
    //else
        //loaded = false;

    reader->Delete();
	return loaded;
}

bool DGVImageVTK::saveVTK(const QString filename)
{
	vtkStructuredPointsWriter* writer = vtkStructuredPointsWriter::New();

    if(!imageDataLoaded)
        arrayToData();

    writer->SetInput(imageData);
    writer->SetFileName(filename.toStdString().c_str());
    writer->Write();
    writer->Delete();

	return true;
}

bool DGVImageVTK::openTGA()
{
    unsigned char* targaimage;
    int dims[3];

    targaimage = (unsigned char*) tga_load(name.toStdString().c_str(), &dims[0], &dims[1], TGA_TRUECOLOR_32);

    int extents[6] = {0, dims[0]-1, 0, dims[1]-1, 0, 0};

    if (targaimage == NULL)
    {
        printError("Failed to read Targa image!");
        printError(tga_error_string(tga_get_last_error()));
        return false;
    }

    imageData = vtkImageData::New();
    imageData->SetDimensions(dims);
    imageData->SetExtent(extents);
    imageData->SetScalarTypeToUnsignedChar();
    imageData->SetNumberOfScalarComponents(4);

    for(int j = 0; j < dims[0]; j ++)
        for(int k = 0; k < dims[1]; k ++)
        {
            imageData->SetScalarComponentFromDouble(j, k, 0, 0, targaimage[(j*dims[0] + k)*4]);
            imageData->SetScalarComponentFromDouble(j, k, 0, 1, targaimage[(j*dims[0] + k)*4 + 1]);
            imageData->SetScalarComponentFromDouble(j, k, 0, 2, targaimage[(j*dims[0] + k)*4 + 2]);
            imageData->SetScalarComponentFromDouble(j, k, 0, 3, targaimage[(j*dims[0] + k)*4 + 3]);
        }

    loaded = true;
    imageData->Update();
    imageDataLoaded = true;
    imageDataSource = true;
    arrayLoaded = false;
    refresh(); // to update colour, not best solution.

    free(targaimage);
    return loaded;
}

bool DGVImageVTK::saveTGA(const QString filename)
{
	unsigned char* targaimage;
    int dims[3];

    if(!imageDataLoaded)
        arrayToData();

    imageData->GetDimensions(dims);

    targaimage = (unsigned char*) tga_create(dims[0], dims[1], TGA_TRUECOLOR_32);

    if(imageData->GetNumberOfScalarComponents() == 1)
    {
        for(int j = 0; j < dims[0]; j ++)
            for(int k = 0; k < dims[1]; k ++)
            {
                targaimage[(j*dims[0] + k)*4] = imageData->GetScalarComponentAsDouble(j,k,0,0);
                targaimage[(j*dims[0] + k)*4 + 1] = imageData->GetScalarComponentAsDouble(j,k,0,0);
                targaimage[(j*dims[0] + k)*4 + 2] = imageData->GetScalarComponentAsDouble(j,k,0,0);
                targaimage[(j*dims[0] + k)*4 + 3] = 255; //Alpha
            }
        printInfo("Single Component Image Written.");
    }
    else if(imageData->GetNumberOfScalarComponents() == 3)
    {
        for(int j = 0; j < dims[0]; j ++)
            for(int k = 0; k < dims[1]; k ++)
            {
                targaimage[(j*dims[0] + k)*4] = imageData->GetScalarComponentAsDouble(j,k,0,0);
                targaimage[(j*dims[0] + k)*4 + 1] = imageData->GetScalarComponentAsDouble(j,k,0,1);
                targaimage[(j*dims[0] + k)*4 + 2] = imageData->GetScalarComponentAsDouble(j,k,0,2);
                targaimage[(j*dims[0] + k)*4 + 3] = 255; //Alpha
            }
    }
    else if(imageData->GetNumberOfScalarComponents() > 3)
    {
        for(int j = 0; j < dims[0]; j ++)
            for(int k = 0; k < dims[1]; k ++)
            {
                targaimage[(j*dims[0] + k)*4] = imageData->GetScalarComponentAsDouble(j,k,0,0);
                targaimage[(j*dims[0] + k)*4 + 1] = imageData->GetScalarComponentAsDouble(j,k,0,1);
                targaimage[(j*dims[0] + k)*4 + 2] = imageData->GetScalarComponentAsDouble(j,k,0,2);
                targaimage[(j*dims[0] + k)*4 + 3] = imageData->GetScalarComponentAsDouble(j,k,0,3); //Alpha
            }
    }
    else
    {
        printError("Unsupported Number of Components in Targa Write.");
        free(targaimage);
        return false;
    }

    if (!tga_write_raw(filename.toStdString().c_str(), dims[0], dims[1], targaimage, TGA_TRUECOLOR_32))
    {
        printError("Failed to write Targa image!");
        printError(tga_error_string(tga_get_last_error()));
        return false;
    }

    free(targaimage);
	return true;
}

void DGVImageVTK::complexValuedMode(bool complexValues)
{
    complexData = complexValues;
    absoluteMagnitudeAct->setDisabled(!complexValues);
    absoluteMagnitudeAct->setChecked(true);
    imaginaryPartAct->setDisabled(!complexValues);
    realPartAct->setDisabled(!complexValues);
    phaseAct->setDisabled(!complexValues);
}

void DGVImageVTK::setData(Array<imageType,2> &data)
{
    if(data.rows() > 0 && data.cols() > 0)
    {
        imageArray.resizeAndPreserve(data.shape());
        imageArray = data;
        arrayLoaded = true;
        externalData = false;
        loaded = true;
        volume = false;
        DGVImageVTK::arrayToData();
        imageDataSource = false;
    }
}

void DGVImageVTK::setData(Array<imageType,3> &data)
{
    if(data.rows() > 0 && data.cols() > 0 && data.depth() > 0)
    {
        imageData = Blitz.arrayToVTKImageData(data);
        arrayLoaded = false;
        loaded = true;
        imageDataLoaded = true;
        imageDataSource = true;
        externalData = false;
        volume = true;
        dataToArray();
    }
}

void DGVImageVTK::setData(Array< complex<imageType>,2 > &data)
{
    if(data.rows() > 0 && data.cols() > 0)
    {
        complexValuedMode(true);
        imageComplexArray.resizeAndPreserve(data.shape());
        imageComplexArray = data;
        arrayLoaded = true;
        externalData = false;
        loaded = true;
        volume = false;
        DGVImageVTK::arrayToData();
        imageDataSource = false;
    }
}

void DGVImageVTK::setData(Array< complex<imageType>,3 > &data)
{
    if(data.rows() > 0 && data.cols() > 0)
    {
        complexValuedMode(true);
        imageData = Blitz.arrayToVTKImageData(data);
        arrayLoaded = false;
        loaded = true;
        imageDataLoaded = true;
        imageDataSource = true;
        externalData = false;
        volume = true;
        dataToArray();
    }
}

void DGVImageVTK::setData(vtkImageData *newData)
{
    imageData = newData;
    arrayLoaded = false;
    loaded = true;
    imageDataLoaded = true;
    imageDataSource = true;
    externalData = true;
    dataToArray();
}

void DGVImageVTK::setDataSize(int rows, int cols)
{
    if(complexData)
    {
        imageComplexArray.resize(rows,cols);
        imageComplexArray = 0;
    }
    else
    {
        imageArray.resize(rows,cols);
        imageArray = 0;
    }
}

void DGVImageVTK::SetInput(vtkImageData *newData)
{
    setData(newData);
}

void DGVImageVTK::setConsole(DGVConsole *newConsole)
{
    if(newConsole != NULL)
    {
        console = newConsole;
        consoleAssigned = true;
    }
}

Array<imageType,2>& DGVImageVTK::data()
{
    if(imageDataSource && !arrayLoaded)
        DGVImageVTK::dataToArray();
    if(viewing && volume)
        sliceToData();
    return imageArray;
}

Array< complex<imageType>,2 >& DGVImageVTK::dataComplex()
{
    return imageComplexArray;
}

Array<imageType,2>& DGVImageVTK::dataViewing()
{
    if(!complexData)
        return data();

    imageArray.resize(imageComplexArray.shape());
    if(absoluteMagnitudeAct->isChecked())
    {
        setWindowTitle(strippedNamePrefix() + "[Abs]");
        imageArray = abs(imageComplexArray);
        return imageArray;
    }
    else if(imaginaryPartAct->isChecked())
    {
        setWindowTitle(strippedNamePrefix() + "[Imag]");
        imageArray = imag(imageComplexArray);
        return imageArray;
    }
    else if(realPartAct->isChecked())
    {
        setWindowTitle(strippedNamePrefix() + "[Real]");
        imageArray = real(imageComplexArray);
        return imageArray;
    }
    else if(phaseAct->isChecked())
    {
        setWindowTitle(strippedNamePrefix() + "[Phase]");
        imageArray = arg(imageComplexArray);
        return imageArray;
    }
    else
        return imageArray;
}

DGVVolumePlotVTK* DGVImageVTK::getVolume()
{
    if(!volume || !enableVolumes)
        printError("Possible Error: Image is not a volume or volume plot is not enabled.");
    return volViewer;
}

imageType DGVImageVTK::getMaxValue()
{
    if(imageDataSource && !arrayLoaded)
        DGVImageVTK::dataToArray();

    return Blitz.getMax();
}

imageType DGVImageVTK::getMinValue()
{
    if(imageDataSource && !arrayLoaded)
        DGVImageVTK::dataToArray();

    return Blitz.getMin();
}

imageType DGVImageVTK::getMean()
{
    if(imageDataSource && !arrayLoaded)
        DGVImageVTK::dataToArray();

    return Blitz.getMean();
}

QString DGVImageVTK::strippedName()
{
    return QFileInfo(name).fileName();
}

QString DGVImageVTK::strippedNamePrefix()
{
    return "Image: " + QFileInfo(name).fileName();
}

void DGVImageVTK::addToContextMenu(QVector<QMenu*> &menus)
{
    menusToAdd = menus;
}

void DGVImageVTK::generateVolume()
{
    if(loaded && volume)
    {
        volViewer = new DGVVolumePlotVTK(parentWidget());
        volViewer->setName(name);
        if(outputPort != NULL)
            volViewer->SetInputConnection(outputPort);
        imageData->Update();
        volViewer->setData(imageData);
        volViewer->generatePlot();
        volViewer->generateAxes();
        volViewer->show();

        emit volumePlotAvailable(volViewer);
    }
    else
        printError("Volume data not detected. Check the image data.");
}

void DGVImageVTK::generateImage()
{
    ///Is data manually entered and not set by setData()?
    checkDataIsManual();

    if(loaded)
    {
        int bounds[6];
        if(!viewing)
        {
            viewer = ImageViewer::New();
            viewing = true;
        }
        printInfo("Generating Image");
        imageData->GetExtent(bounds);

        magnify = vtkImageMagnify::New();
        magnify->SetInput(imageData);
        if(bounds[1]+1 < minWindowSize && bounds[3]+1 < minWindowSize)
            magnify->SetMagnificationFactors(minWindowSize/(bounds[1]+1),minWindowSize/(bounds[3]+1),1);
        else
            magnify->SetMagnificationFactors(1,1,1);
        magnify->InterpolateOff();

        if(bounds[5] > 1) ///If 3rd dimension in image then display volume plot too
        {
            volume = true;
            if(enableVolumes)
            {
                printInfo("Found 3D Image Data. Also showing volume plot.");
                generateVolume();
            }
        }
        else
            printInfo("Didn't find 3D Image Data. Not showing volume plot.");

        outputPort = magnify->GetOutputPort();
        viewer->SetInputConnection(magnify->GetOutputPort());
        viewer->GetRenderer()->ResetCamera();
        viewer->SetColorLevel(Blitz.getMean());
        viewer->SetColorWindow(Blitz.getMean()); //Also good: (max-min)/2+min
        QVTKWidget::SetRenderWindow(viewer->GetRenderWindow());
        viewer->SetupInteractor(QVTKWidget::GetRenderWindow()->GetInteractor());

        setupEvents();

        if(bounds[1] > minWindowSize && bounds[3] > minWindowSize)
        {
            viewer->SetSize(bounds[1],bounds[3]);
            QVTKWidget::resize(bounds[1],bounds[3]);
        }
        else
        {
            viewer->SetSize(minWindowSize,minWindowSize);
            QVTKWidget::resize(minWindowSize,minWindowSize);
        }

        magnify->Delete();
    }
}

void DGVImageVTK::scan()
{
    if(loaded && viewing)
    {
        if ( timerId >= 0 ) //Stop
        {
            killTimer(timerId);
            timerId = -1;
        }
        else //Start
        {

            bool ok;
            double interval = QInputDialog::getDouble(this, tr("Scan Interval Time"), tr("Interval (ms):"), 250, 0, 1000000, 1, &ok);

            if(ok)
            {
                timerInterval = qRound(interval);

                if (timerInterval >= 0 )
                {
                    viewer->SetSlice(0);
                    viewer->GetRenderer()->ResetCamera();
                    timerId = startTimer(timerInterval);
                }
            }
        }
    }
}

void DGVImageVTK::table()
{
    ///Is data manually entered and not set by setData()?
    checkDataIsManual();

    if(loaded)
    {
        dataTable = new DGVTableVTK(parentWidget());
        QString tmp;

        printInfo("Constructing Image Data Table");
        if( (imageDataSource && !arrayLoaded) || volume ) //!< Force reload just in case slice changed
            DGVImageVTK::dataToArray();
        dataTable->setName(name);
        if(complexData)
            dataTable->setData(imageComplexArray);
        else
            dataTable->setData(imageArray);
        DGVImageVTK::connectTable(dataTable);
        dataTable->show(); //!< \todo If user does not set parent of image, leak here. Use QObjectCleanupHandler perhaps.

        emit tableAvailable(dataTable);
    }
}

void DGVImageVTK::mergedSurfacePlot()
{
    ///Is data manually entered and not set by setData()?
    checkDataIsManual();

    if(loaded)
    {
        surfacePlot = new DGVSurfacePlotVTK(parentWidget());

        if(imageDataSource) ///\todo Suface and scalars not aligned properly
            DGVImageVTK::dataToArray();
        else
            DGVImageVTK::arrayToData();
        surfacePlot->setName(name);
        if(consoleAssigned)
            surfacePlot->setConsole(console);
        surfacePlot->setData(dataViewing());
        surfacePlot->generatePlotMergedWith(imageData);
        surfacePlot->generateAxes();
        surfacePlot->show(); //!< \todo If user does not set parent of image, leak here. Use QObjectCleanupHandler perhaps.
        externalData = true; //!< \todo Leak when surface plot doesn't delete imageData.

        emit surfacePlotAvailable(surfacePlot);
    }
}

void DGVImageVTK::clip()
{
    if(loaded)
    {
        int bounds[6];

        if(!imageDataLoaded)
            DGVImageVTK::arrayToData();

        imageData->GetExtent(bounds);

        bool okStartRows, okEndRows, okStartCols, okEndCols;
        int newStartRows = QInputDialog::getInteger(this, tr("Clip Image to subset"),
                                          tr("Start at Row:"), bounds[0], bounds[0], bounds[1], 1, &okStartRows);
        int newEndRows = QInputDialog::getInteger(this, tr("Clip Image to subset"),
                                          tr("End at Row:"), bounds[1], bounds[0], bounds[1], 1, &okEndRows);
        int newStartCols = QInputDialog::getInteger(this, tr("Clip Image to subset"),
                                          tr("Start at Column:"), bounds[2], bounds[2], bounds[3], 1, &okStartCols);
        int newEndCols = QInputDialog::getInteger(this, tr("Clip Image to subset"),
                                          tr("End at Column:"), bounds[3], bounds[2], bounds[3], 1, &okEndCols);

        int newStartDepth = 0, newEndDepth = 1;

        if(volume)
        {
            newStartDepth = QInputDialog::getInteger(this, tr("Clip Image to subset"),
                                              tr("Start at Depth:"), bounds[4], bounds[4], bounds[5], 1, &okStartCols);
            newEndDepth = QInputDialog::getInteger(this, tr("Clip Image to subset"),
                                              tr("End at Depth:"), bounds[5], bounds[4], bounds[5], 1, &okEndCols);
        }

        QString tmp1, tmp2;

        if(okStartRows && okEndRows && okStartCols && okEndCols && newStartCols < newEndCols && newStartRows < newEndRows)
        {
            vtkImageClip *clipper = vtkImageClip::New();

            printInfo("Clip image data to: " + tmp1.setNum(newEndRows-newStartRows) + "x" + tmp2.setNum(newEndCols-newStartCols));
            clipper->ClipDataOn();
            clipper->SetInput(imageData);
            if(volume)
                clipper->SetOutputWholeExtent(newStartRows, newEndRows, newStartCols, newEndCols, newStartDepth, newEndDepth);
            else
                clipper->SetOutputWholeExtent(newStartRows, newEndRows, newStartCols, newEndCols, bounds[4], bounds[5]);
            clipper->UpdateWholeExtent();

            imageData = clipper->GetOutput();
            if(volume)
                imageData->SetDimensions(newEndRows-newStartRows, newEndCols-newStartCols, newEndCols-newStartCols);
            else
                imageData->SetDimensions(newEndRows-newStartRows, newEndCols-newStartCols, bounds[5]-bounds[4]);
            bounds[0] = 0;
            bounds[1] = newEndRows-newStartRows;
            bounds[2] = 0;
            bounds[3] = newEndCols-newStartCols;
            if(volume)
            {
                bounds[4] = 0;
                bounds[5] = newEndDepth-newStartDepth;
            }
            imageData->SetExtent(bounds); ///\todo problem with extents matching between imageData and imageArray
            //imageData->Update();
            imageDataSource = true;
            dataToArray(); ///Force array to match

            magnify = vtkImageMagnify::New();
            magnify->SetInput(imageData);
            if(bounds[1]+1 < minWindowSize && bounds[3]+1 < minWindowSize)
                magnify->SetMagnificationFactors(minWindowSize/(bounds[1]+1),minWindowSize/(bounds[3]+1),1);
            else
                magnify->SetMagnificationFactors(1,1,1);
            magnify->InterpolateOff();

            if(!viewing)
            {
                viewer = ImageViewer::New();
                viewing = true;
            }

            viewer->SetInputConnection(magnify->GetOutputPort());
            viewer->Render();

            magnify->Delete();
        }
    }
}

void DGVImageVTK::histogram()
{
    if(loaded)
    {
        if(!imageDataLoaded)
            DGVImageVTK::arrayToData();
        if(!arrayLoaded)
            DGVImageVTK::dataToArray();

        vtkImageAccumulate *hist = vtkImageAccumulate::New();

        hist->SetInput(imageData);
        hist->SetComponentExtent(Blitz.getMin(),Blitz.getMax(),0,0,0,0); ///Single component/channel only
        hist->Update();
        hist->Print(cerr);

        int span = abs(Blitz.getMax() - Blitz.getMin()) + 1;

        if(span < 100000) ///Too big, stop
        {
            Array<imageType,1> xData(span), bins(span);
            QString tmp;
            firstIndex x;

            printInfo("Using Custom Histogramer");
            printInfo("Number of bins: " + tmp.setNum(span));

            dataViewing(); ///Force loading into array (needed for complex data)
            xData = x + Blitz.getMin();
            bins = 0;
            for(int j = 0; j < imageArray.rows(); j ++)
                for(int k = 0; k < imageArray.cols(); k ++)
                    bins( imageArray(j,k) - Blitz.getMin() ) ++;

            DGVPlot *plot = new DGVPlot(this);

            plot->setName(name);
            plot->setWindowTitle("Histogram: " + strippedName());
            plot->createCurvePlot(xData,bins,"Histogram: " + strippedName());

            hist->Delete();
            emit plotAvailable(plot);
        }
        else
        {
            DGVPlotVTK *plot = new DGVPlotVTK(this);

            printInfo("Using VTK Histogramer");
            plot->setName(name);
            plot->setWindowTitle("Histogram: " + strippedName());
            plot->setData(hist->GetOutput());
            plot->setRanges(hist->GetMin()[0],hist->GetMax()[0],0,0);
            //plot->setRanges(Blitz.getMin(),Blitz.getMax(),0,0);
            //plot->setRanges(0,0,0,0); ///Forces auto range computation (VTK)
            plot->generatePlot();

            hist->Delete();
            emit plotAvailable(plot);
        }
    }
}

void DGVImageVTK::refresh()
{
    ///Is data manually entered and not set by setData()?
    checkDataIsManual();

    if(loaded)
    {
        ///Load from inital source
        if(arrayLoaded)
            DGVImageVTK::arrayToData();
        else if(imageDataSource)
            DGVImageVTK::dataToArray();
        else
            return;
        printInfo("Refreshing Image: " + strippedName());
        DGVImageVTK::generateImage();
        imageData->Update();
        if(viewing)
            viewer->Render();
    }
}

void DGVImageVTK::renameData()
{
    bool ok;

    QString newName = QInputDialog::getText(this, tr("Rename Data"), tr("New Name: "), QLineEdit::Normal, name, &ok);

    if(ok)
        setName(newName);
}

void DGVImageVTK::updateData(int row, int col)
{
    //if(dataTable->isLinkedToImage() && !dataTable->isViewChanging())
    if(!dataTable->isViewChanging())
    {
        //cerr << "Updating (" << row << "," << col << ") of the ";
        if(!complexData)
        {
            //cerr << "Real Table Data" << endl;
            imageArray(row,col) = dataTable->item(row,col)->text().toDouble();
        }
        else
        {
            imageType tmpValue;

            //cerr << "Complex Table Data: ";
            if(dataTable->viewingImaginaryPart())
            {
                //cerr << "Imaginary Part" << endl;
                tmpValue = imageComplexArray(row,col).real();
                imageComplexArray(row,col) = complex<imageType>(tmpValue,dataTable->item(row,col)->text().toDouble());
            }
            else if(dataTable->viewingRealPart())
            {
                //cerr << "Real Part" << endl;
                tmpValue = imageComplexArray(row,col).imag();
                imageComplexArray(row,col) = complex<imageType>(dataTable->item(row,col)->text().toDouble(),tmpValue);
            }
            /*else if(dataTable->viewingMagnitude() || dataTable->viewingPhase())
            {
                dataTable->setItem(row,col,abs(imageComplexArray(row,col)));
                printWarning("Ignoring changes to Absolute Magnitude and Phase. Please make changes in Imaginary and Real parts.");
            }*/
        }
        imageDataLoaded = false;
        imageDataSource = false;
        arrayLoaded = true; //!< Make array the source now
    }
}

void DGVImageVTK::interpolateDisplay()
{
    if(loaded && viewing)
    {
        if(viewer->GetImageActor()->GetInterpolate()) ///\todo Bug: Interpolate toggled manually
        {
            printInfo("Disabling Interpolation");
            viewer->GetImageActor()->InterpolateOff();
            interpolateAct->setChecked(false);
        }
        else
        {
            printInfo("Enabling Interpolation");
            viewer->GetImageActor()->InterpolateOn();
            interpolateAct->setChecked(true);
        }
    }
}

void DGVImageVTK::meanGreyScale()
{
    if(loaded && viewing)
    {
        viewer->SetColorLevel(Blitz.getMean());
        viewer->SetColorWindow(Blitz.getMean()); //Also good: (max-min)/2+min
    }
}

void DGVImageVTK::incrementSlice()
{
    /*if(loaded && viewing)
    {
        if(viewer->GetSlice()+1 <= viewer->GetSliceMax())
        {
            cerr << "Incrementing from Slice " << viewer->GetSlice() << " to Slice " << viewer->GetSlice()+1 << endl;
            viewer->SetSlice(viewer->GetSlice()+1);
        }
    }*/
}

void DGVImageVTK::decrementSlice()
{
    /*if(loaded && viewing)
    {
        if(viewer->GetSlice()-1 >= viewer->GetSliceMin())
        {
            cerr << "Decrementing from Slice " << viewer->GetSlice() << " to Slice " << viewer->GetSlice()-1 << endl;
            viewer->SetSlice(viewer->GetSlice()-1);
        }
    }*/
}

void DGVImageVTK::viewToXYPlane()
{
    if(!permuted)
    {
        permute = ImagePermute::New();
        permuted = true;
    }

    permute->SetInput(imageData);
    permute->SetFilteredAxes(0,1,2);
    permute->Update();

    if(viewing)
    {
        viewer->SetInputConnection(permute->GetOutputPort());
        viewer->Render();
    }
}

void DGVImageVTK::viewToZXPlane()
{
    if(!permuted)
    {
        permute = ImagePermute::New();
        permuted = true;
    }

    permute->SetInput(imageData);
    permute->SetFilteredAxes(1,2,0);
    permute->Update();

    if(viewing)
    {
        viewer->SetInputConnection(permute->GetOutputPort());
        viewer->Render();
    }
}

void DGVImageVTK::viewToZYPlane()
{
    if(!permuted)
    {
        permute = ImagePermute::New();
        permuted = true;
    }

    permute->SetInput(imageData);
    permute->SetFilteredAxes(2,0,1);
    permute->Update();

    if(viewing)
    {
        viewer->SetInputConnection(permute->GetOutputPort());
        viewer->Render();
    }
}

void DGVImageVTK::createActions()
{
    absoluteMagnitudeAct = new QAction(this);
    absoluteMagnitudeAct->setText(QApplication::translate("Image", "Absolute &Magnitude", 0, QApplication::UnicodeUTF8));
    absoluteMagnitudeAct->setShortcut(tr("Alt+m"));
    absoluteMagnitudeAct->setCheckable(true);

    imaginaryPartAct = new QAction(this);
    imaginaryPartAct->setText(QApplication::translate("Image", "&Imaginary Part", 0, QApplication::UnicodeUTF8));
    imaginaryPartAct->setShortcut(tr("Alt+i"));
    imaginaryPartAct->setCheckable(true);

    realPartAct = new QAction(this);
    realPartAct->setText(QApplication::translate("Image", "&Real Part", 0, QApplication::UnicodeUTF8));
    realPartAct->setShortcut(tr("Alt+r"));
    realPartAct->setCheckable(true);

    phaseAct = new QAction(this);
    phaseAct->setText(QApplication::translate("Image", "&Phase/Argument", 0, QApplication::UnicodeUTF8));
    phaseAct->setShortcut(tr("Alt+p"));
    phaseAct->setCheckable(true);

    complexGroup = new QActionGroup(this);
    complexGroup->addAction(absoluteMagnitudeAct);
    complexGroup->addAction(imaginaryPartAct);
    complexGroup->addAction(realPartAct);
    complexGroup->addAction(phaseAct);
    complexGroup->setDisabled(!complexData);
}

void DGVImageVTK::createConnections()
{
    connect(absoluteMagnitudeAct, SIGNAL(triggered()), this, SLOT(refresh()));
    connect(imaginaryPartAct, SIGNAL(triggered()), this, SLOT(refresh()));
    connect(realPartAct, SIGNAL(triggered()), this, SLOT(refresh()));
    connect(phaseAct, SIGNAL(triggered()), this, SLOT(refresh()));
}

void DGVImageVTK::connectTable(DGVTableVTK *child)
{
    child->linkToImageData(true); //!< Necessary to ensure updating of table is correctly done.
    QObject::connect(child,SIGNAL(cellChanged(int,int)), this, SLOT(updateData(int,int)));
}

void DGVImageVTK::dataToArray()
{
    if(loaded && imageDataSource)
    {
        int* Size = imageData->GetDimensions();
        bool success;
        QString tmp1, tmp2, tmp3;

        ///Diagnostics
        printInfo("Dimensions of the picture read: " + tmp1.setNum(Size[0]) + " x " + tmp2.setNum(Size[1]));

        if(complexData)
            arrayToData(); //!< Load part being viewed

        if(viewing && volume) /// Load into main Blitz Array
            success = sliceToData();
        else
            success = Blitz.vtkImageDataToArray(imageData,imageArray);

        if(success)
        {
            if(!volume)
                arrayLoaded = true;
            printInfo("Min and Max Pixel Values are " + tmp1.setNum(Blitz.getMin()) + " and " + tmp2.setNum(Blitz.getMax()) + ". Mean: " + tmp3.setNum(Blitz.getMean()));
        }
        else
            printError("vtk ImageData to Blitz Array cast failed");
    }
}

bool DGVImageVTK::sliceToData()
{
    QString tmp1;
    bool success;

    if(sliceInView != viewer->GetSlice())
    {
        printInfo("Loading New Slice: " + tmp1.setNum(viewer->GetSlice()));
        sliceInView = viewer->GetSlice();
        success = Blitz.vtkImageDataToArray(imageData,viewer->GetSlice(),imageArray);
    }
    else
        success = true;

    return success;
}

void DGVImageVTK::arrayToData()
{
    if(loaded && arrayLoaded)
    {
        QString tmp1, tmp2, tmp3;

        imageData = Blitz.arrayToVTKImageData(dataViewing());
        imageDataLoaded = true;
        //imageDataSource = false;
        externalData = false;

        printInfo("Min and Max Pixel Values are " + tmp1.setNum(Blitz.getMin()) + " and " + tmp2.setNum(Blitz.getMax()) + ". Mean: " + tmp3.setNum(Blitz.getMean()));
    }
}

void DGVImageVTK::checkDataIsManual()
{
    if(((imageArray.rows() > 0 && imageArray.cols() > 0) || (imageComplexArray.rows() > 0 && imageComplexArray.cols() > 0) )  && !loaded && !imageDataSource)
    {
        arrayLoaded = true;
        loaded = true;
        DGVImageVTK::arrayToData();
        imageDataSource = false;
    }
}

void DGVImageVTK::addContextMenuExtras()
{
    for(int j = 0; j < menusToAdd.size(); j ++)
        contextMenu->addAction(menusToAdd[j]->menuAction());
}

void DGVImageVTK::setupEvents()
{
    if(viewing)
    {
        vtkCallbackCommand* callback = vtkCallbackCommand::New();
        vtkCallbackCommand* callbackWheelUp = vtkCallbackCommand::New();
        vtkCallbackCommand* callbackWheelDown = vtkCallbackCommand::New();
        vtkEventForwarderCommand* forward = vtkEventForwarderCommand::New();

        callback->SetCallback(handleKeys);
        callbackWheelUp->SetCallback(handleWheelUp);
        callbackWheelDown->SetCallback(handleWheelDown);
        forward->SetTarget(viewer); //!< Redirect event to viewer to allow access of its members
        ///Bind the Up and Down keys
        QVTKWidget::GetRenderWindow()->GetInteractor()->AddObserver(vtkCommand::KeyPressEvent,forward,1.0); //!< Forward keyboard events
        //QVTKWidget::GetRenderWindow()->GetInteractor()->AddObserver(vtkCommand::MouseWheelForwardEvent,forward,1.0); //!< Forward keyboard events
        //QVTKWidget::GetRenderWindow()->GetInteractor()->AddObserver(vtkCommand::MouseWheelBackwardEvent,forward,1.0); //!< Forward keyboard events
        viewer->AddObserver(vtkCommand::KeyPressEvent,callback,1.0); //!< Observe this command
        //viewer->AddObserver(vtkCommand::MouseWheelForwardEvent,callbackWheelUp,1.0); //!< Observe this command
        //viewer->AddObserver(vtkCommand::MouseWheelBackwardEvent,callbackWheelDown,1.0); //!< Observe this command
        ///Unbind the right mouse button events as Qt uses context menu.
        QVTKWidget::GetRenderWindow()->GetInteractor()->RemoveObservers(vtkCommand::RightButtonPressEvent);
        QVTKWidget::GetRenderWindow()->GetInteractor()->RemoveObservers(vtkCommand::RightButtonReleaseEvent);

        callback->Delete();
        callbackWheelUp->Delete();
        callbackWheelDown->Delete();
        forward->Delete();
    }
}

void DGVImageVTK::setupViewerFromReader()
{
    int bounds[6];
    QString tmp1, tmp2;

	imageData->GetExtent(bounds);
    printInfo("Image Size: " + tmp1.setNum(bounds[1]+1) + "x" + tmp2.setNum(bounds[3]+1));

    magnify = vtkImageMagnify::New();
    magnify->SetInput(imageData);
    if(bounds[1]+1 < minWindowSize && bounds[3]+1 < minWindowSize)
        magnify->SetMagnificationFactors(minWindowSize/(bounds[1]+1),minWindowSize/(bounds[3]+1),1);
    else
        magnify->SetMagnificationFactors(1,1,1);
    magnify->InterpolateOff();

    viewer->SetInputConnection(magnify->GetOutputPort());
    viewer->GetRenderer()->ResetCamera();
    QVTKWidget::SetRenderWindow(viewer->GetRenderWindow());
    viewer->SetupInteractor(QVTKWidget::GetRenderWindow()->GetInteractor());

    setupEvents();

    if(bounds[1]+1 > minWindowSize && bounds[3]+1 > minWindowSize)
    {
        viewer->SetSize(bounds[1]+1,bounds[3]+1);
        QVTKWidget::resize(bounds[1]+1,bounds[3]+1);
    }
    else
    {
        viewer->SetSize(minWindowSize-1,minWindowSize-1);
        QVTKWidget::resize(minWindowSize-1,minWindowSize-1);
    }

    loaded = true;
    arrayLoaded = false;
    imageDataSource = true;
    imageDataLoaded = true;

    magnify->Delete();
}

void DGVImageVTK::contextMenuEvent(QContextMenuEvent *event)
{
    contextMenu = new QMenu(this); //!< Only exists for the duration of the context selection

    ///Extra (preconnected) menus to add.
    addContextMenuExtras();

    ///Setup Data action
    ///Table
    contextMenu->addSeparator();
    tableAct = contextMenu->addAction(tr("Table"));
    tableAct->setToolTip("Display raw data table of the image data");
    tableAct->setStatusTip("Display raw data table of the image data");
    tableAct->setShortcut(tr("Ctrl+t"));
    connect(tableAct, SIGNAL(triggered()), this, SLOT(table()));
    ///SPlot
    surfacePlotAct = contextMenu->addAction(tr("Merged Surface Plot with Image"));
    surfacePlotAct->setToolTip("Surface Plot based on scalar warping and image data");
    surfacePlotAct->setStatusTip("Surface Plot based on scalar warping and image data");
    surfacePlotAct->setShortcut(tr("Ctrl+p"));
    connect(surfacePlotAct, SIGNAL(triggered()), this, SLOT(mergedSurfacePlot()));
    scanAct = contextMenu->addAction(tr("Start/Stop Scan of Volume"));
    scanAct->setToolTip("Scan through the image slices");
    scanAct->setStatusTip("Scan through the image slices");
    scanAct->setShortcut(tr("Ctrl+a"));
    scanAct->setEnabled(volume);
    connect(scanAct, SIGNAL(triggered()), this, SLOT(scan()));
    volumeAct = contextMenu->addAction(tr("Volumetric Plot"));
    volumeAct->setToolTip("Volume Plot based on scalar values of the image slices");
    volumeAct->setStatusTip("Volume Plot based on scalar values of the image slices");
    volumeAct->setShortcut(tr("Ctrl+v"));
    volumeAct->setEnabled(volume);
    connect(volumeAct, SIGNAL(triggered()), this, SLOT(generateVolume()));
    histAct = contextMenu->addAction(tr("Histogram"));
    histAct->setShortcut(tr("Ctrl+h"));
    connect(histAct, SIGNAL(triggered()), this, SLOT(histogram()));

    ///Interp
    contextMenu->addSeparator();
    interpolateAct = contextMenu->addAction(tr("Interpolation"));
    interpolateAct->setCheckable(true);
    if(viewing)
        interpolateAct->setChecked(viewer->GetImageActor()->GetInterpolate());
    interpolateAct->setShortcut(tr("Ctrl+i"));
    connect(interpolateAct, SIGNAL(triggered()), this, SLOT(interpolateDisplay()));
    meanGreyAct = contextMenu->addAction(tr("Colour Level to Mean"));
    meanGreyAct->setShortcut(tr("Ctrl+l"));
    connect(meanGreyAct, SIGNAL(triggered()), this, SLOT(meanGreyScale()));
    if(volume)
        clipAct = contextMenu->addAction(tr("Crop Volume"));
    else
        clipAct = contextMenu->addAction(tr("Crop Image"));
    clipAct->setShortcut(tr("Ctrl+c"));
    connect(clipAct, SIGNAL(triggered()), this, SLOT(clip()));

    ///Change View of Volume
    viewMenu = contextMenu->addMenu("View"); //!< Only exists for the duration of the context selection
    viewXY = viewMenu->addAction(tr("xy-plane"));
    viewXY->setShortcut(tr("Ctrl+z"));
    viewXY->setEnabled(volume);
    connect(viewXY, SIGNAL(triggered()), this, SLOT(viewToXYPlane()));
    viewZX = viewMenu->addAction(tr("zx-plane"));
    viewZX->setShortcut(tr("Ctrl+y"));
    viewZX->setEnabled(volume);
    connect(viewZX, SIGNAL(triggered()), this, SLOT(viewToZXPlane()));
    viewZY = viewMenu->addAction(tr("zy-plane"));
    viewZY->setShortcut(tr("Ctrl+x"));
    connect(viewZY, SIGNAL(triggered()), this, SLOT(viewToZYPlane()));
    viewZY->setEnabled(volume);

    ///Setup Toggle Options
    contextMenu->addSeparator()->setText(tr("Complex View"));
    ///Abs Display
    contextMenu->addAction(absoluteMagnitudeAct);
    ///Imag Display
    contextMenu->addAction(imaginaryPartAct);
    ///Real Display
    contextMenu->addAction(realPartAct);
    ///Phase Display
    contextMenu->addAction(phaseAct);
    complexGroup->setDisabled(!complexData);

    contextMenu->addSeparator();
	///Setup Close action
	///Refresh
	saveAct = contextMenu->addAction(tr("Save Data as Image..."));
	saveAct->setShortcut(tr("Ctrl+s"));
	connect(saveAct, SIGNAL(triggered()), this, SLOT(saveImage()));
	saveStackAct = contextMenu->addAction(tr("Save Data as Image Stack..."));
	saveStackAct->setShortcut(tr("Ctrl+Shift+s"));
	saveStackAct->setEnabled(volume);
	connect(saveStackAct, SIGNAL(triggered()), this, SLOT(saveStack()));
	refreshAct = contextMenu->addAction(tr("Refresh"));
	refreshAct->setShortcut(tr("F5"));
	connect(refreshAct, SIGNAL(triggered()), this, SLOT(refresh()));
	renameAct = contextMenu->addAction(tr("Rename"));
	renameAct->setShortcut(tr("Ctrl+r"));
	connect(renameAct, SIGNAL(triggered()), this, SLOT(renameData()));
	///Close
	closeAct = contextMenu->addAction(tr("Close"));
	closeAct->setShortcut(tr("Ctrl+w"));
	connect(closeAct, SIGNAL(triggered()), this, SLOT(close()));

	contextMenu->exec(event->globalPos());
}

void DGVImageVTK::timerEvent(QTimerEvent *event)
{
    if(loaded && viewing)
    {
        int bounds[6];

        imageData->GetExtent(bounds);

        if(viewer->GetSlice()+1 <= viewer->GetSliceMax())
        {
            //cerr << "Slice " << viewer->GetSlice() << ", ";
            viewer->SetSlice(viewer->GetSlice()+1);
            viewer->GetRenderer()->ResetCamera();
        }
        else
        {
            killTimer(timerId);
            timerId = -1;
        }
    }
}

void DGVImageVTK::printError(QString msg)
{
    if(verboseMode)
    {
        if(consoleAssigned)
        {
            console->printError(msg);
        }
        else
            cerr << msg.toStdString() << endl;
    }
}

void DGVImageVTK::printWarning(QString msg)
{
    if(verboseMode)
    {
        if(consoleAssigned)
        {
            console->printWarning(msg);
        }
        else
            cerr << msg.toStdString() << endl;
    }
}

void DGVImageVTK::printInfo(QString msg)
{
    if(verboseMode)
    {
        if(consoleAssigned)
        {
            console->printInfo(msg);
        }
        else
            cerr << msg.toStdString() << endl;
    }
}

void handleKeys(vtkObject* obj, unsigned long, void *clientData, void *callData)
{
    vtkImageViewer2* view = vtkImageViewer2::SafeDownCast(obj);

    string keyPressed = view->GetRenderWindow()->GetInteractor()->GetKeySym();

    if(keyPressed == "Up")
    {
        if(view->GetSlice()+1 <= view->GetSliceMax())
        {
            cerr << "Incrementing from Slice " << view->GetSlice() << " to Slice " << view->GetSlice()+1 << endl;
            view->SetSlice(view->GetSlice()+1);
            view->GetRenderer()->ResetCamera();
        }
    }
    else if(keyPressed == "Down")
    {
        if(view->GetSlice()-1 >= view->GetSliceMin())
        {
            cerr << "Decrementing from Slice " << view->GetSlice() << " to Slice " << view->GetSlice()-1 << endl;
            view->SetSlice(view->GetSlice()-1);
            view->GetRenderer()->ResetCamera();
        }
    }
}

void handleWheelUp(vtkObject* obj, unsigned long, void *clientData, void *callData)
{
    vtkImageViewer2* view = vtkImageViewer2::SafeDownCast(obj);

    cerr << "Wheel Up!" << endl;
    view->GetRenderer()->GetActiveCamera()->Dolly(1.25);
    view->Render();
}

void handleWheelDown(vtkObject* obj, unsigned long, void *clientData, void *callData)
{
    vtkImageViewer2* view = vtkImageViewer2::SafeDownCast(obj);

    cerr << "Wheel Down!" << endl;
    view->GetRenderer()->GetActiveCamera()->Dolly(0.8);
    view->Render();
}
